﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Permissions;

namespace Net35.Xrm.Sdk
{
	[SerializableAttribute]
	[ComVisibleAttribute(false)]
	[HostProtectionAttribute(SecurityAction.LinkDemand, Synchronization = true,
		ExternalThreading = true)]

	internal class ConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue>,
		ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>,
		IDictionary, ICollection, IEnumerable
	{
		volatile Dictionary<TKey, TValue> _data;
		System.Threading.ReaderWriterLockSlim _dataLock = new System.Threading.ReaderWriterLockSlim(System.Threading.LockRecursionPolicy.SupportsRecursion);

		#region CTOR
		public ConcurrentDictionary()
		{
			_data = new Dictionary<TKey, TValue>();
		}
		ConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection)
		{
			_data = new Dictionary<TKey, TValue>();
			foreach (var pair in collection)
			{
				_data.Add(pair.Key, pair.Value);
			}
		}
		ConcurrentDictionary(IEqualityComparer<TKey> comparer)
		{
			_data = new Dictionary<TKey, TValue>(comparer);
		}
		ConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer)
		{
			_data = new Dictionary<TKey, TValue>(comparer);
			foreach (var pair in collection)
			{
				_data.Add(pair.Key, pair.Value);
			}
		}
		ConcurrentDictionary(Int32 concurrencyLevel, Int32 capacity)
		{
			_data = new Dictionary<TKey, TValue>(capacity);
		}
		ConcurrentDictionary(Int32 concurrencyLevel, IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer)
		{
			_data = new Dictionary<TKey, TValue>(comparer);
			foreach (var pair in collection)
			{
				_data.Add(pair.Key, pair.Value);
			}
		}
		ConcurrentDictionary(Int32 concurrencyLevel, Int32 capacity, IEqualityComparer<TKey> comparer)
		{
			_data = new Dictionary<TKey, TValue>(capacity, comparer);
		}
		#endregion

		#region Specific To ConcurrentDictionary
		public bool IsEmpty
		{
			get
			{
				using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Read))
				{
					return _data.Count == 0;
				}
			}
		}
		public TValue AddOrUpdate(TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory)
		{
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Write))
			{
				if (_data.ContainsKey(key))
				{
					_data[key] = updateValueFactory(key, _data[key]);
				}
				else
				{
					_data.Add(key, addValueFactory(key));
				}
				return _data[key];
			}
		}
		public TValue AddOrUpdate(TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueFactory)
		{
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Write))
			{
				if (_data.ContainsKey(key))
				{
					_data[key] = updateValueFactory(key, _data[key]);
				}
				else
				{
					_data.Add(key, addValue);
				}
				return _data[key];
			}
		}
		public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
		{
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Write))
			{
				if (_data.ContainsKey(key))
				{
					_data[key] = valueFactory(key);
				}
				else
				{
					_data.Add(key, valueFactory(key));
				}
				return _data[key];
			}
		}
		public TValue GetOrAdd(TKey key, TValue value)
		{
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Write))
			{
				if (_data.ContainsKey(key))
				{
					_data[key] = value;
				}
				else
				{
					_data.Add(key, value);
				}
				return _data[key];
			}
		}
		public bool TryRemove(TKey key, out TValue value)
		{
			value = default(TValue);
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Write))
			{
				if (_data.ContainsKey(key))
				{
					value = _data[key];
					return true;
				}
				else
				{
					return false;
				}
			}
		}
		#endregion

		#region interfaceImplementations
		public IEnumerator GetEnumerator()
		{
			return _data.GetEnumerator();
		}

		public void CopyTo(Array array, int index)
		{
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Read))
			{
				if (index < _data.Count)
				{
					List<KeyValuePair<TKey, TValue>> items = new List<KeyValuePair<TKey, TValue>>();
					foreach (var pair in _data.Skip(index))
					{
						items.Add(pair);
					}
					Array.Copy(items.ToArray(), array, items.Count);
				}
			}
		}

		public int Count
		{
			get
			{
				using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Read))
				{
					return _data.Count;
				}
			}
		}

		public bool IsSynchronized
		{
			get
			{
				return false;
			}
		}

		public object SyncRoot
		{
			get { return null; }
		}

		public void Add(object key, object value)
		{
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Write))
			{
				_data.Add((TKey)key, (TValue)value);
			}
		}

		public void Clear()
		{
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Write))
			{
				_data.Clear();
			}
		}

		public bool Contains(object key)
		{
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Read))
			{
				return _data.ContainsKey((TKey)key);
			}
		}

		IDictionaryEnumerator IDictionary.GetEnumerator()
		{
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Read))
			{
				return _data.GetEnumerator();
			}
		}

		public bool IsFixedSize
		{
			get
			{
				return false;
			}
		}

		public bool IsReadOnly
		{
			get
			{
				return false;
			}
		}

		public ICollection Keys
		{
			get
			{
				using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Read))
				{
					return _data.Keys;
				}
			}
		}

		public void Remove(object key)
		{
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Write))
			{
				_data.Remove((TKey)key);
			}
		}

		public ICollection Values
		{
			get
			{
				using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Read))
				{
					return _data.Values;
				}
			}
		}

		public object this[object key]
		{
			get
			{
				using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Read))
				{
					return _data[(TKey)key];
				}
			}
			set
			{
				using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Write))
				{
					_data[(TKey)key] = (TValue)value;
				}
			}
		}

		IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
		{
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Read))
			{
				return _data.GetEnumerator();
			}
		}

		public void Add(KeyValuePair<TKey, TValue> item)
		{
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Write))
			{
				_data.Add(item.Key, item.Value);
			}
		}

		public bool Contains(KeyValuePair<TKey, TValue> item)
		{
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Write))
			{
				return _data.ContainsKey(item.Key);
			}
		}

		public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
		{
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Read))
			{
				Array.Copy(_data.Skip(arrayIndex).ToArray(), array, _data.Count - arrayIndex);
			}
		}

		public bool Remove(KeyValuePair<TKey, TValue> item)
		{
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Write))
			{
				return _data.Remove(item.Key);
			}
		}

		public void Add(TKey key, TValue value)
		{
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Write))
			{
				_data.Add(key, value);
			}
		}

		public bool ContainsKey(TKey key)
		{
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Read))
			{
				return _data.ContainsKey(key);
			}
		}

		ICollection<TKey> IDictionary<TKey, TValue>.Keys
		{
			get
			{
				using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Read))
				{
					return _data.Keys;
				}
			}
		}

		public bool Remove(TKey key)
		{
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Write))
			{
				return _data.Remove(key);
			}
		}

		public bool TryGetValue(TKey key, out TValue value)
		{
			value = default(TValue);
			using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Read))
			{
				if (_data.ContainsKey(key))
				{
					value = _data[key];
					return true;
				}
				else
				{
					return false;
				}
			}
		}

		ICollection<TValue> IDictionary<TKey, TValue>.Values
		{
			get
			{
				using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Read))
				{
					return _data.Values;
				}
			}
		}

		public TValue this[TKey key]
		{
			get
			{
				using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Read))
				{
					return _data[key];
				}
			}
			set
			{
				using (new ReaderWriterSlim_LockHelper(_dataLock, LockType.Write))
				{
					_data[key] = value;
				}
			}
		}
		#endregion
	}
}